<?php

namespace app\api\service\cms;
use app\api\model\event\Merchant;
use app\api\model\event\MerchantLog;
use app\api\model\event\PayOrder;
use app\api\model\Gear;
use app\api\service\token\LoginToken;
use app\lib\exception\event\MerchantException;
use app\lib\pay\Pay;
use think\Db;
use think\Exception;
use think\facade\Env;
require Env::get("app_path").'../extend/phpqrcode/phpqrcode.php';

class PayService extends Pay
{
    private static $instance;

    public static function getInstance(): PayService
    {
        if (!self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * @param $info
     * @throws MerchantException
     */
    public function payQrcode($info){
        $login = LoginToken::getInstance();
        $uid = $login->getCurrentUid();
        $merchant_id = Merchant::where("uid",$uid)->value("id");
        if ($merchant_id==null){
            throw new MerchantException([
                'msg' => '商户不存在',
                'error_code' => 20001
            ]);
        }

        # 手动输入充值
        $pay = $info['pay']??0;
        if ($pay % 1000 !== 0){
            throw new MerchantException([
                'msg' => '不是1000的倍数',
                'error_code' => 20002
            ]);
        }


        # 挡位选择充值
        $gear_id = $info['gear_id']??'';


        $params = [
            'body' => '我是一个支付的标题',
            'detail' => '我是一个支付的描述',
            'out_trade_no' => time() . mt_rand(1000,9999), // 生产订单号 通常在加入购物车 还未支付的时候生成
            'total_fee' => 1,// 单位是分
            'notify_url'=>'https://rockets.youdnr.com/notify',
//            'notify_url' => 'http://danzhao.ynzzwl.com/huidiao.php',// 微信回调 - 必须是外网的地址
            // 'openid' => '',特定人支付 选填
            'appid' => $this->app_id,
            'mch_id' => $this->mch_id,
            // 'spbill_create_ip' => '127.0.0.1' // 防止钓鱼，指定IP
            'trade_type' => 'NATIVE' // 交易类型 [JSAPI,NATIVE,APP]
        ];
        $balance = 0;
        if (!empty($gear_id)){
            $gear = Gear::where("id",$gear_id)->where("status",1)->find();
            if (!$gear)return false;
            $params['total_fee'] = $gear['total_fee'];
            $balance = $gear['balance'];
            $params['body'] = $gear['name'];
            $params['detail'] = $gear['name'];
        }else{
            if ($pay == 0)return false;
            $params['total_fee'] = $pay * 100;
            $balance = $pay ; # 一比一
            $params['body'] = "充值:".$pay."元 到账:".$balance."火箭豆";
            $params['detail'] = "充值:".$pay."元 到账:".$balance."火箭豆";
        }





        $result = $this->prepare($params);
        # 二维码链接有效期两个小时
        $url = $result['code_url'];
        DB::startTrans();
        try {
            $data = [
                "out_trade_no"=>$params['out_trade_no'],
                "total_fee"=>$params['total_fee'],
                "body"=>$params['body'],
                "detail"=>$params['detail'],
                'merchant_id'=>$merchant_id??0,
                "istype"=>2,
                "balance"=>$balance
            ];
            PayOrder::create($data);
            // 提交事务
            Db::commit();
        }catch (Exception $e) {
            // 回滚事务
            Db::rollback();
        }

        $qRcode  = new \QRcode();
        $qRcode->png($url, false, 'L', 10);
        $imageString = base64_encode(ob_get_contents());//将图片流存入缓存并加密赋值给变量
        ob_end_clean();
        header('Content-Type:text/html;charset=utf-8');// QRimage::png 会覆盖此header头，所以要重新声明一下，否则解析不了html代码。
        return [
            "url"=> "data:image/png;base64,".$imageString,
            "order"=>$params['out_trade_no']
        ];
    }

    #下单
    private function prepare($config){
        $config['nonce_str'] = uniqid();
        $config['sign'] = $this->generateSign($config,$this->api_v2); // 签名
        $xml = $this->toXml($config); // 转化为XML格式
        // 统一下单
        $result = $this->curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', $xml);
        return $this->xmlToArray($result);
    }


    #签名
    private function generateSign($params,$key,$encrypt = 'md5'){
        ksort($params); // 将集合排序
        $params['key'] = $key; // 拼接KEY
        $str = http_build_query($params); // 转化为URL字符串
        return strtoupper(md5(urldecode($str))); // md5加密
    }

    # toXml
    private function toXml($params){
        $xml = "<xml>";
        foreach($params as $key => $value){
            if(is_numeric($value)){
                $xml .= "<$key>$value</$key>";
            }else{
                $xml .= "<$key><![CDATA[$value]]></$key>";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    #  xml to array
    private function xmlToArray($xml){
        if(!$xml){ throw new WxPayException("xml数据异常！"); }
        libxml_disable_entity_loader(true); // 禁止引用外部xml实体
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    private function curlPost($url = '', $postData = '', $options = array()){
        if (is_array($postData)) {
            $postData = http_build_query($postData);
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
        if (!empty($options)) {
            curl_setopt_array($ch, $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }



    /**
     * 支付回调
     */
    public function notify()
    {
        $info = file_get_contents('php://input'); //7.0以上
//        $file = 'data.txt'; // 文本文件的路径和名称
//        file_put_contents($file, $info);
        $order = $this->xmlToArray($info);
        $trade['order'] = $order['out_trade_no'];
        $trade['total_fee'] = $order['total_fee'];
        if ($order['return_code'] == 'SUCCESS'){
            // 签名处理
            DB::startTrans();
            try {
                //            $mySign = $this->generateSign($order,$this->api_v2);
                $pay = PayOrder::where("out_trade_no",$trade['order'])->where("status",0)->findOrEmpty();
                if (!$pay->isEmpty()){
                    $pay->save(['status'=>1]);
                    # 上账火箭豆
                    Merchant::where("id",$pay['merchant_id'])->setInc("balance",$pay['balance']);
                    #
                    $log_data = [
                        "balance"=>$pay['balance'],
                        "merchant_id"=>$pay['merchant_id'],
                        "content"=>"充值火箭豆".$pay['balance'],
                        "type"=>1
                    ];
                    MerchantLog::create($log_data);
                    # websocket 通知
                    $this->pubsub($pay['out_trade_no']);
                        // 提交事务
                    // 更改订单状态 更改订单状态之前需要先去查询订单状态，如果是已支付，就不需要处理了
                    // 具体代码
                    // 通知微信我们已接收到信息，不要再给我们发该条订单的请求，不然会给你发9次消息
                    // 频率：15，15，30，180，1800，1800，1800，3600
                    echo "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
//            $mes = array(
//                'code' => 'SUCCESS',
//                'message' => '成功'
//            );
//            return xml($mes);
                }
                Db::commit();
            }catch (Exception $e) {
                // 回滚事务
                Db::rollback();
            }
        }
    }

    public function pubsub($channel){
        $data=array(
            "appkey"=>"BC-585f5e8861424cdeb942d6b46c0fd14d",
            "channel"=>$channel,
            "content"=>"支付成功");//post参数
        $url = "https://rest-hz.goeasy.io/v2/pubsub/publish";

        $content = http_build_query($data);
        $length = strlen($content);
        $options = array(
            'http' => array(
                'method' => 'POST',
                'header' =>
                    "Content-type: application/x-www-form-urlencoded\r\n" .
                    "Content-length: $length \r\n",
                'content' => $content

            )

        );
        file_get_contents($url, false, stream_context_create($options));

    }
}
